using Microsoft.Extensions.Logging;
using Quartz;
using Vin.Extension.Framework.Dto;
using Vin.Extension.Framework.Implement;
using Vin.Extension.Framework.Quartz.Entities;
using Vin.Extension.Framework.Quartz.Repository;
using Vin.Extension.Framework.Quartz.ServiceInterface;
using Vin.Tool.Attributes;
using Vin.Tool.Core.ConvertCategory;
using Vin.Tool.Core.DateTimeCategory;
using Vin.Tool.Core.ExceptionCategory;
using Vin.Tool.Enums;

namespace Vin.Extension.Framework.Quartz.Service;

[Service(Lifetime = Lifetime.Singleton)]
public class ExecuteJobService :
    VinFrameworkBaseRecordEntityServiceAsync<SysJob, VinFrameworkBaseListDto, SysJobRepository>, IExecuteJobService
{
    [Inject] private readonly SysJobRecordRepository _sysJobRecordRepository = null!;

    public const string JobKey = "job";

    public const string StartTimeKey = "startTime";

    public const string RecordIdKey = "recordId";

    public async Task<bool> ExecuteAsync(IJobExecutionContext context)
    {
        var jobId = context.JobDetail.Key.Name.ToLong();
        var job = await _baseRepository.GetByIdAsync(jobId);
        if (job == null) return false;
        context.Put(JobKey, job);
        context.Put(StartTimeKey, DateTime.Now);

        // 增加记录
        var record = new SysJobRecord()
        {
            JobId = jobId,
            JobName = job.Name,
            GroupId = job.GroupId,
            ExecuteTime = DateTime.Now,
            LoopCount = job.LoopCount,
            CurrentLoopCount = job.CompletedLoopCount + 1,
            AssemblyPath = job.AssemblyPath,
            AssemblyName = job.AssemblyName,
            ClassName = job.ClassName,
            Parameters = job.Parameters,
            Status = JobRecordStatus.Running,
            Exception = "",
        };
        await _sysJobRecordRepository.SaveOrUpdateAsync(record);

        // 传递数据
        context.Put(RecordIdKey, record.Id!.Value);

        return true;
    }

    /// <summary>
    /// 任务被否决
    /// </summary>
    public Task<bool> VetoedAsync(IJobExecutionContext context)
    {
        Logger.LogInformation("{0}任务被否决", context.JobDetail.Key);
        return Task.FromResult(true);
    }

    /// <summary>
    /// 任务执行完成
    /// </summary>
    public async Task<bool> CompleteAsync(IJobExecutionContext context, JobExecutionException? jobException)
    {
        var job = context.Get(JobKey) as SysJob;
        if (job == null) return false;

        // 更新任务
        await UpdateJobAsync(job, jobException);

        // 更新记录
        await UpdateRecordAsync(context, jobException);

        return true;
    }

    /// <summary>
    /// 更新任务
    /// </summary>
    private async Task UpdateJobAsync(SysJob job, JobExecutionException? jobException)
    {
        // 更新任务
        var updateJob = new SysJob()
        {
            LastExecuteTime = DateTime.Now.ToTimestamp(false),
            CompletedLoopCount = job.CompletedLoopCount + 1
        };

        // 有异常
        if (jobException != null)
        {
            updateJob.ErrorCount = job.ErrorCount + 1;
        }

        // 修改任务状态
        if (updateJob.CompletedLoopCount >= job.LoopCount)
        {
            updateJob.Status = JobStatus.Completed;
        }

        await _baseRepository.ExecuteUpdateableAsync(updateJob, m => m.Id == job.Id);
    }

    /// <summary>
    /// 更新记录
    /// </summary>
    private async Task UpdateRecordAsync(IJobExecutionContext context, JobExecutionException? jobException)
    {
        var recordId = context.Get(RecordIdKey) as long?;
        var startTime = context.Get(StartTimeKey) as DateTime?;
        var costTime = (DateTime.Now - startTime!.Value).Milliseconds;
        var status = jobException == null ? JobRecordStatus.Success : JobRecordStatus.Faild;

        // 更新记录
        var updateRecord = new SysJobRecord()
        {
            CostTime = costTime,
            Status = status
        };

        // 有异常
        if (jobException != null)
        {
            updateRecord.Exception = jobException.ExceptionMessage();
        }

        // 修改记录
        await _sysJobRecordRepository.ExecuteUpdateableAsync(updateRecord, m => m.Id == recordId);
    }
}